Overview

In the previous assignment, we took the RNASeq data from the publication Cannabidiol inhibits SARS-COV-2 replication and promotes the host innate immune response by Nguyen et al., cleaned the data, performed normalization, gene identifier mapping, and some additional preliminary analysis on the dataset. As suggested by the title, the authors of the publication studied the effects of cannabidiol on SARS-CoV-2 replication and host’s immune response. The authors hypothesized and concluded that cannabidiol inhibits SARS-CoV-2 replication by up-regulating the host IRE1α ribonuclease endoplasmic reticulum (ER) stress response and interferon signaling pathways. The experiment conditions from in the dataset can be categorized into

with 3 replicates for each test condition.

We obtained the dataset from GEO with ID GSE168797, associated with the study Cannabidiol inhibits SARS-COV-2 replication and promotes the host innate immune response published on Science Advances. Out of the total of 57832 genes, 13705 remained after removing low counts and genes with duplicate identifiers.

Data normalization is performed using TMM with the edgeR package. Normalization corrects the large deviation of the means of untreated groups from the groups treated with CBD, while still preserving some of the characteristics of the original sample distribution.

counts_density_normalized <- apply(log2(normalized_counts), 2, density)

xlim <- 0; ylim <- 0
for (i in 1:length(counts_density_normalized)) {
  xlim <- range(c(xlim, counts_density_normalized[[i]]$x));
  ylim <- range(c(ylim, counts_density_normalized[[i]]$y))
}
cols <- rainbow(length(counts_density_normalized))
ltys <- rep(1, length(counts_density_normalized))

plot(counts_density_normalized[[1]], xlim = xlim, ylim = ylim, type = "n", ylab = "Smoothing density of log2-CPM", main = "Distribution of count density for each experiment condition (normalized)", cex.lab = 0.85)
for (i in 1:length(counts_density_normalized))
  lines(counts_density_normalized[[i]], col = cols[i], lty = ltys[i])
legend("topright", colnames(data2plot), col=cols, lty=ltys, cex=0.75, border="blue", text.col = "green4", merge = TRUE, bg = "gray90")

We observe separation of groups after normalization.

limma::plotMDS(log2(normalized_counts), labels=rownames(samples), col = c("darkgreen","blue")[factor(samples$cbd_treatment)], main = "MDS plot after normalization showing distances between samples")
legend("topleft", legend=rownames(samples), fill=c("darkgreen","blue")[factor(samples$cbd_treatment)])

We will revisit this figure later when performing differential gene expression analysis.

Identifier mapping was performed using the package biomaRt with data from Ensembl. The Ensembl gene IDs in the original dataset are mapped to the corresponding HUGO gene symbols. The final dataset included 13705 genes with unique identifiers.

Setup

In this section, we import and install the necessary packages for this assignment, in which we will conduct a differential expression analysis using the normalized dataset and a thresholded over-representation analysis.

if (!requireNamespace("magrittr"))
  install.packages("magrittr")
if (!requireNamespace("circlize", quietly = TRUE))
    install.packages("circlize")
if (!requireNamespace("ComplexHeatmap", quietly = TRUE))
    BiocManager::install("ComplexHeatmap")
if (!requireNamespace("gprofiler2", quietly = TRUE))
    BiocManager::install("gprofiler2")

# edgeR and other packages are imported in A1, which is sourced at the beginning of the notebook

library(magrittr)

knitr::opts_chunk$set(message = FALSE)

Differential Expression Analysis using edgeR

In this section, we are going to perform differential expression analysis.

Let us first examine the normalized data.

knitr::kable(d[1:10,1:6]$counts, caption = "Normalized gene counts") %>% kableExtra::kable_styling("striped")
Normalized gene counts
CBD_infect-1 CBD_infect-2 CBD_infect-3 Veh_infect-1 Veh_infect-2 Veh_infect-3
ENSG00000000003 1797 1912 2055 1210 1190 1139
ENSG00000000419 2820 2834 3028 1984 1985 1981
ENSG00000000457 615 518 526 819 777 845
ENSG00000000460 439 523 553 779 781 849
ENSG00000000971 982 1090 1024 1303 1030 1275
ENSG00000001036 2699 3005 3054 1468 1412 1387
ENSG00000001084 8435 8888 9079 4723 4449 4754
ENSG00000001167 1178 1299 1388 1599 1796 1687
ENSG00000001460 907 712 739 285 292 311
ENSG00000001461 1325 1177 1161 551 517 429
knitr::kable(d[1:10,1:6]$samples, caption = "Sample groups") %>% kableExtra::kable_styling("striped")
Sample groups
group lib.size norm.factors
CBD_infect-1 CBD 38561370 1.4614028
CBD_infect-2 CBD 38330456 1.4411943
CBD_infect-3 CBD 39366212 1.4163787
Veh_infect-1 Veh 113140193 0.3877739
Veh_infect-2 Veh 116155068 0.3823093
Veh_infect-3 Veh 127114090 0.3454226

Workflow

We will be calculating differential expression following this general workflow:

  1. Create deisng matrix. We will ensure that all factors that could contribute to differential expression is accounted for in the design matrix.

  2. Use a mean-variance plot to confirm that the data follows the negative binomial distribution assumption for using the quasi-likelihood model in edgeR.

  3. Fit our data to the model and calculated p-value and corrected p-value.

  4. Use a threshold to extract differentially expressed genes with statistically significant differences.

  5. Plot and visualize.

Creating the Design Matrix

In order to perform statistical testing, we need a design matrix the defines our model. Notice that in our dataset, there are three factors:

  1. Treatment with CBD
  2. Infection status (infected or not)
  3. Patient

Hence, ideally, we would like to account for all three factors in our design matrix.

model_design <- model.matrix(~ samples$patient + samples$cbd_treatment + samples$infected)
model_design[,4] <- !model_design[,4]
colnames(model_design)[4] <- "samples$cbd_treatmentCBD"
knitr::kable(model_design, caption = "Design matrix") %>%
  kableExtra::kable_styling("striped")
Design matrix
(Intercept) samples$patient2 samples$patient3 samples$cbd_treatmentCBD samples$infectedmock
1 0 0 1 0
1 1 0 1 0
1 0 1 1 0
1 0 0 0 0
1 1 0 0 0
1 0 1 0 0
1 0 0 1 1
1 1 0 1 1
1 0 1 1 1
1 0 0 0 1
1 1 0 0 1
1 0 1 0 1

Distribution of Data

For our downstream analysis, we are going to use edgeR. We chose edgeR because it is specifically designed for RNASeq data. However, one important underlying assumption for using the quasi-likelihood model is that the data follows a negative binomial distribution. We need to verify that our dataset indeed meets that assumption.

Let us the calculate the dispersion and plot to visualize the mean-variance relationship.

d <- edgeR::DGEList(counts = normalized_counts_annot[,3:ncol(normalized_counts_annot)], group = samples$cbd_treatment)
d <- edgeR::estimateDisp(d, model_design)
edgeR::plotMeanVar(d,
                   show.raw.vars = TRUE,
                   show.tagwise.vars = TRUE,
                   NBline = TRUE,
                   show.ave.raw.vars = TRUE,
                   show.binned.common.disp.vars = TRUE,
                   main = "Mean-Variance Plot for Variance and Dispersion of Our Data")
# display legend
legend("topleft", 
       legend=c("Raw Data", "Tagwise Dispersion", "Average Raw Variances", 
                "Binned Common Dispersion", "Negative Binomial Line"), 
       col = c("grey", "lightblue", "maroon", "red", "dodgerblue2"), pch=c(1,1,4,4,NA), lty=c(0,0,0,0,1), lwd=c(1,1,1,1,2), cex=0.6)

As demonstrated by the mean-variance plot above, we can see that the dispersion and variance of our data indeed roughly follows the negative binomial distribution.

Analysis Using edgeR

Now, we have created the design matrix and verified the assumption for the data to be negative-binomially distributed, we can proceed to the next stage of our analysis and perform statistical testing and corrections to ensure that we only get significantly differentially expressed genes. We used the quasi-likelihood models since our dataset is from an RNASeq experiment and quasi-likelihood models are best suited to handle RNASeq data.

fit <- edgeR::glmQLFit(d, model_design)

Once we have fit the model, we can proceed to calculate differential expression. Recall that our goal is to verify the role of cannabidiol in affecting the replication ability of SARS-CoV-2, so we will be using cbd_treatment as the contrast.

qlf <- edgeR::glmQLFTest(fit, coef = 'samples$cbd_treatmentCBD')
qlf_diff_exp <- edgeR::topTags(qlf, sort.by = "PValue", n = nrow(normalized_counts_annot))
knitr::kable(qlf_diff_exp[1:10,]$table, type="html", digits = 20, caption = "Top differentially expressed genes") %>%
  kableExtra::kable_styling("striped")
Top differentially expressed genes
logFC logCPM F PValue FDR
DNAJB9 3.296477 6.632298 2376.139 1.174076e-13 1.225637e-09
IGFBP1 4.315555 7.225448 2191.283 1.788598e-13 1.225637e-09
HSPA5 3.721184 11.833327 1920.096 3.552926e-13 1.623095e-09
CRELD2 3.023632 6.669438 1395.860 1.858463e-12 5.742205e-09
KIF20A -2.327554 5.891384 1344.930 2.253309e-12 5.742205e-09
ADGRF4 2.315894 4.618755 1287.278 2.827571e-12 5.742205e-09
GABARAPL1 2.351451 6.414219 1278.222 2.932903e-12 5.742205e-09
NDRG4 1.776359 6.129025 1133.516 5.463712e-12 9.159587e-09
HERPUD1 3.400467 8.195296 1061.159 7.686235e-12 9.159587e-09
STC2 2.742212 6.444858 1059.247 7.758262e-12 9.159587e-09

We can examine the number of genes pass the threshold and correction. We are using 0.05 as the threshold for p-value as it is commonly used in practice.

# number of genes that passed the threshold
sum(qlf_diff_exp$table$PValue < 0.05)
[1] 5501
# number of genes that passed correction
sum(qlf_diff_exp$table$FDR < 0.05)
[1] 4424

The threshold of 0.05 gives us quite a lot of genes, in order to get more meaningful hits in the downstream gene enrichment analysis, we will make the thershold more stringent.

# number of genes that passed the threshold
sum(qlf_diff_exp$table$PValue < 0.01 & abs(qlf_diff_exp$table$logFC) > 1.5)
[1] 513
# number of genes that passed correction
sum(qlf_diff_exp$table$FDR < 0.01 & abs(qlf_diff_exp$table$logFC) > 1.5)
[1] 442

Visualization of Differentially Expressed Genes

Now we can retrieve the list of differential expressed genes and visualize using different plots. We will first plot them on a volcano plot. Each gene is represented by a point in the plot. The horizontal axis of the plot is the \(\log_2\) fold change and the vertical axis is the \(-\log_{10}p\) which indicates the statistical significance of each gene (how likely the differential is due to actual biological variation).

volcano_color_palette = rep('gray', times = nrow(qlf_diff_exp$table))
volcano_color_palette[qlf_diff_exp$table$logFC < 0 & qlf_diff_exp$table$FDR < 0.01 & abs(qlf_diff_exp$table$logFC) > 1.5] <- 'blue'
volcano_color_palette[qlf_diff_exp$table$logFC > 0 & qlf_diff_exp$table$FDR < 0.01 & abs(qlf_diff_exp$table$logFC) > 1.5] <- 'red'

plot(qlf_diff_exp$table$logFC, 
     -log(qlf_diff_exp$table$PValue, base=10), 
     col = volcano_color_palette,
     xlab = "log2 fold change",
     ylab = "-log10 p",
     main = "Volcano plot showing upregulated and downregulated genes"
    )

legend("topright", legend=c("Downregulated in CBD treated cells","Upregulated in CBD treated cells", "Not significant"),fill = c("blue", "red", "grey"), cex = 0.5)

Next, we will visualize the upregulated and downregulated genes across different test conditions using a heatmap.

diff_exp_lst <- qlf_diff_exp$table[qlf_diff_exp$table$FDR < 0.01 & abs(qlf_diff_exp$table$logFC),]
diff_exp_lst$hgnc_symbol <- rownames(diff_exp_lst)

# normalized counts of differentially expressed genes
mat_count_normalized_diff <- normalized_counts_annot[diff_exp_lst$hgnc_symbol, 3:ncol(normalized_counts_annot)]
# rescale the counts
mat_count_normalized_diff <- t(scale(t(mat_count_normalized_diff)))
heatmap_color_palette <- circlize::colorRamp2(c(min(mat_count_normalized_diff), 0, max(mat_count_normalized_diff)),
                                              c("blue", "white", "red"))
ComplexHeatmap::Heatmap(as.matrix(mat_count_normalized_diff),
                        name = "scaled normalized count",
                        cluster_rows = TRUE,
                        cluster_columns = TRUE, 
                        show_row_dend = FALSE, 
                        show_column_dend = FALSE, 
                        col = heatmap_color_palette, 
                        show_column_names = TRUE, 
                        show_row_names = FALSE, 
                        show_heatmap_legend = TRUE,
                        column_title = "Samples",
                        row_title = "Genes",
                        use_raster = TRUE)

Discussion

  1. Initially, I used the p-value of 0.05 as it is widely used in practice. This gives us 5501 genes prior to correction. This is quite a large number of genes, so we changed the p-value threshold to 0.01 to limit the number of genes included. We further added the criteria that a gene must have an absolute log fold change greater than 1.5. By doing so, we are contraining ourselves to only get the genes that are highly differentially expressed with high statistical significance.

  2. For correction, we used Benjamini-Hochberg. The two main methods discussed for correcting family-wise false discovery rate are Bonferroni and Benjamini-Hochberg correction. We want to get meaningful hits without excluding statistically significant ones. Bonferroni’s method is overly stringent so it is not quite suitable for our purposes. Using Benjamini-Hochberg will give us a richer set of genes that we can use for downstream analysis.

  3. Volcano plot shown above. Note that we also applied the restriction that only genes with aboslute log fold change greater than 1.5 are to be included.

  4. There is significant clustering within conditions. This suggests that the test condition (CBD v.s. no CBD) does have a significant effect on gene expressions.

Thresholded Overrepresentation Analysis using g:Profiler

For the final part of this assignment, we will perform a thresholded overrepresentation analysis using g:Profiler. In the previous section, we have compiled a list of differentially expressed genes. Here, we want to further divide them into upregulated and downregulated genes.

upregulated_gene_lst <- diff_exp_lst[diff_exp_lst$logFC > 0,]
downregulated_gene_lst <- diff_exp_lst[diff_exp_lst$logFC < 0,]

We use the R package for g:Profiler to perform the gene enrichment analysis. For correction, we used FDR as it is less stringent than Bonferroni and is introduced as the preferred correction method in class. We used GO Biological Process, GO Molecular Function, and KEGG as those are the ones used by the author of the original publication. For a more detailed overview of the workflow, please refer to the Discussion subsection located at the end of this section.

Upregulated Genes

up_top_terms_all <- gprofiler2::gost(query = rownames(upregulated_gene_lst), 
                                  organism = "hsapiens", 
                                  exclude_iea = TRUE,
                                  correction_method = "fdr",
                                  sources = c("GO:BP", "GO:MF", "KEGG"))

up_top_terms <- data.frame(
  term_name = up_top_terms_all$result$term_name[up_top_terms_all$result$term_size < 500 &
                                               up_top_terms_all$result$term_size > 2],
  term_id = up_top_terms_all$result$term_id[up_top_terms_all$result$term_size < 500 &
                                           up_top_terms_all$result$term_size > 2],
  source = up_top_terms_all$result$source[up_top_terms_all$result$term_size < 500 &
                                         up_top_terms_all$result$term_size > 2]
)

knitr::kable(up_top_terms[1:10,], caption = "Top genesets using list of upregulated genes") %>% kableExtra::kable_styling("striped")
Top genesets using list of upregulated genes
term_name term_id source
response to endoplasmic reticulum stress GO:0034976 GO:BP
proteasomal protein catabolic process GO:0010498 GO:BP
ERAD pathway GO:0036503 GO:BP
ubiquitin-dependent ERAD pathway GO:0030433 GO:BP
proteasome-mediated ubiquitin-dependent protein catabolic process GO:0043161 GO:BP
autophagy GO:0006914 GO:BP
process utilizing autophagic mechanism GO:0061919 GO:BP
response to topologically incorrect protein GO:0035966 GO:BP
macroautophagy GO:0016236 GO:BP
endoplasmic reticulum to Golgi vesicle-mediated transport GO:0006888 GO:BP

For context, let’s examine the top term from each data source.

knitr::kable(rbind(up_top_terms[up_top_terms$source == "GO:BP",][1,],
                   up_top_terms[up_top_terms$source == "GO:MF",][1,],
                   up_top_terms[up_top_terms$source == "KEGG",][1,]),
             caption = "Top terms from each data source using list of upregulated genes") %>%
  kableExtra::kable_styling("striped")
Top terms from each data source using list of upregulated genes
term_name term_id source
1 response to endoplasmic reticulum stress GO:0034976 GO:BP
331 unfolded protein binding GO:0051082 GO:MF
401 Protein processing in endoplasmic reticulum KEGG:04141 KEGG

We can visualize the distribution of top terms from each data source using an Manhattan plot. This is the distribution of terms prior to removing large terms with over 500 genes.

gprofiler2::gostplot(up_top_terms_all) %>% plotly::layout(title = "Manhattan plot showing distribution of terms \nfrom each data source using list of upregulated genes", font = list(size = 10))
length(up_top_terms$term_name)
[1] 433

Downregulated Genes

We do the same for the downregualted genes.

down_top_terms_all <- gprofiler2::gost(query = rownames(downregulated_gene_lst), 
                                  organism = "hsapiens", 
                                  exclude_iea = TRUE,
                                  correction_method = "fdr",
                                  sources = c("GO:BP", "GO:MF", "KEGG"))

down_top_terms <- data.frame(
  term_name = down_top_terms_all$result$term_name[down_top_terms_all$result$term_size < 500 &
                                                 down_top_terms_all$result$term_size > 2],
  term_id = down_top_terms_all$result$term_id[down_top_terms_all$result$term_size < 500 &
                                             down_top_terms_all$result$term_size > 2],
  source = down_top_terms_all$result$source[down_top_terms_all$result$term_size < 500 &
                                           down_top_terms_all$result$term_size > 2]
)

knitr::kable(down_top_terms[1:10,], caption = "Top genesets using list of downregulated genes") %>% kableExtra::kable_styling("striped")
Top genesets using list of downregulated genes
term_name term_id source
mitotic nuclear division GO:0140014 GO:BP
sister chromatid segregation GO:0000819 GO:BP
mitotic sister chromatid segregation GO:0000070 GO:BP
nuclear division GO:0000280 GO:BP
chromosome segregation GO:0007059 GO:BP
organelle fission GO:0048285 GO:BP
nuclear chromosome segregation GO:0098813 GO:BP
cell cycle phase transition GO:0044770 GO:BP
regulation of cell cycle phase transition GO:1901987 GO:BP
DNA replication GO:0006260 GO:BP
knitr::kable(rbind(down_top_terms[down_top_terms$source == "GO:BP",][1,],
                   down_top_terms[down_top_terms$source == "GO:MF",][1,],
                   down_top_terms[down_top_terms$source == "KEGG",][1,]),
             caption = "Top terms from each data source using list of downregulated genes") %>%
  kableExtra::kable_styling("striped")
Top terms from each data source using list of downregulated genes
term_name term_id source
1 mitotic nuclear division GO:0140014 GO:BP
474 chromatin binding GO:0003682 GO:MF
535 Cell cycle KEGG:04110 KEGG

Plot the Manhattan plot showing distribution of terms from each data source using list of downregulated genes.

gprofiler2::gostplot(down_top_terms_all) %>% plotly::layout(title = "Manhattan plot showing distribution of terms\n from each data source using list of downregulated genes", font = list(size = 10))
length(down_top_terms$term_name)
[1] 560

All Differentially Expressed Genes

Finally, for all differentially expressed genes.

top_terms_all <- gprofiler2::gost(query = rownames(diff_exp_lst), 
                                  organism = "hsapiens", 
                                  exclude_iea = TRUE,
                                  correction_method = "fdr",
                                  sources = c("GO:BP", "GO:MF", "KEGG"))

top_terms <- data.frame(
  term_name = top_terms_all$result$term_name[top_terms_all$result$term_size < 500 &
                                            top_terms_all$result$term_size > 2],
  term_id = top_terms_all$result$term_id[top_terms_all$result$term_size < 500 &
                                        top_terms_all$result$term_size > 2],
  source = top_terms_all$result$source[top_terms_all$result$term_size < 500 &
                                      top_terms_all$result$term_size > 2]
)

knitr::kable(top_terms[1:10,], caption = "Top genesets using list of all differentially expressed genes") %>% kableExtra::kable_styling("striped")
Top genesets using list of all differentially expressed genes
term_name term_id source
mitotic sister chromatid segregation GO:0000070 GO:BP
mitotic nuclear division GO:0140014 GO:BP
sister chromatid segregation GO:0000819 GO:BP
response to endoplasmic reticulum stress GO:0034976 GO:BP
proteasomal protein catabolic process GO:0010498 GO:BP
organelle fission GO:0048285 GO:BP
chromosome segregation GO:0007059 GO:BP
nuclear chromosome segregation GO:0098813 GO:BP
nuclear division GO:0000280 GO:BP
regulation of cell cycle phase transition GO:1901987 GO:BP
knitr::kable(rbind(top_terms[top_terms$source == "GO:BP",][1,],
                   top_terms[top_terms$source == "GO:MF",][1,],
                   top_terms[top_terms$source == "KEGG",][1,]),
             caption = "Top terms from each data source using list of all differentially expressed genes") %>%
  kableExtra::kable_styling("striped")
Top terms from each data source using list of all differentially expressed genes
term_name term_id source
1 mitotic sister chromatid segregation GO:0000070 GO:BP
514 cadherin binding GO:0045296 GO:MF
583 Protein processing in endoplasmic reticulum KEGG:04141 KEGG
gprofiler2::gostplot(top_terms_all) %>% plotly::layout(title = "Manhattan plot showing distribution of terms from each data source", font = list(size = 10))
length(top_terms$term_name)
[1] 616

Discussion

  1. We used g:Profiler as we have extensively discussed about it in class. It has a nice web-based interface as well as easy-to-use APIs in the form of an R package. Furthermore, the data sources on g:Profiler is frequently updated and include the data sources that we are most interested in.

  2. We used GO Biological Process, GO Molecular Function, and KEGO. We chose these data sources since they are also used by the author of the original paper from which I obtained the dataset. However, since KEGO is a commercial data source, if it was not for that fact that the original paper used it, I would personally refrain from using it. The original author also used other data sources including Canonical Pathways but since it is not part of g:Profiler, we did not include these data sources. The annotation source versions are as follows: Ensembl 105, Ensembl Genomes 52 (database built on 2022-02-14)

  3. For all three analysis (using upregulated, downregulated, all differentially expressed), we used a threshold between 2 and 500. We set the upper bound to 500 because we do not want to include overly broad and generic terms that will not give us meaningful insights into the roles of the differentially expressed genes. The set of upregulated genes returned 433 genesets; the set of downregulated genes returned 560 genesets; the set of all differentially expressed genes returned 616 gene sets.

  4. The result using the whole list (set of all differentially expressed genes) is more predominantly represented by the downregulated genes. It also does not provide a lot of insights into the roles of the genes as it is a mix of drastically different and seemingly unrelated terms (e.g. mitotic nuclear division, organelle fission, response to endoplasmic reticulum stress, etc.). However, by using separating into upregualted and downregulated genes and performing the gene enrichment analysis separately, we get more meaningful terms: the upregualted genes are mostly associated with pathways and processes involving endoplasmic reticulum whereas the downregulated genes are mostly associated with the cell cycle.

Interpretation

  1. Yes. The overrepresentation analysis results support the conclusion discussed in the original paper. The original paper claims that CBD treatment can inhibit SARS-CoV-2 replication through the host’s ER stress response pathway. Although we were not able to verify whether or not this has any affect on SARS-CoV-2 replication, we are able to confirm based on the top terms returned by the overrepresentation analysis that the genes associated with ER stress response are indeed upregulated in the samples treated with CBD.

  2. The conclusion that CBD is correlated to reduced risk or severity of SARS-CoV-2 is supported by testing adult patients as presented by the original paper. There are also other papers who discussed the role of CBD in SARS-CoV-2 infection (van Breeman et al.). The result from our overrepresentation analysis is supported by the original paper, in which the author has also performed gene enrichment analysis with similar outcomes. However, since this is still an early research in this topic and SARS-CoV-2 continues to evolve, there does not seem to be other unrelated groups who could provide additional supporting evidence to our results from the overrepresentation analysis besides the original paper.

Journal

Link to my journal entry for this assignment: https://github.com/bcb420-2022/Kevin_Gao/wiki/Assignment-2

References

LS0tCnRpdGxlOiAiS2V2aW4gR2FvIC0gQXNzaWdubWVudCAyOiBEaWZmZXJlbnRpYWwgR2VuZSBleHByZXNzaW9uIGFuZCBQcmVsaW1pbmFyeSBPUkEiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICBtYXRoamF4OiAiaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvbWF0aGpheC8yLjcuNy9NYXRoSmF4LmpzP2NvbmZpZz1UZVgtTU1MLUFNX0NIVE1MIgotLS0KCiMgT3ZlcnZpZXcKCmBgYHtyIGExX3NvdXJjZSwgaW5jbHVkZT1GQUxTRSxlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpvcHRpb25zKHdhcm49LTEpCmExX291dHB1dCA8LSBrbml0cjo6a25pdF9jaGlsZCgnYXNzaWdubWVudDEuUm1kJywgcXVpZXQgPSBUUlVFKQpgYGAKCkluIHRoZSBwcmV2aW91cyBhc3NpZ25tZW50LCB3ZSB0b29rIHRoZSBSTkFTZXEgZGF0YSBmcm9tIHRoZSBwdWJsaWNhdGlvbiAqQ2FubmFiaWRpb2wgaW5oaWJpdHMgU0FSUy1DT1YtMiByZXBsaWNhdGlvbiBhbmQgcHJvbW90ZXMgdGhlIGhvc3QgaW5uYXRlIGltbXVuZSByZXNwb25zZSogYnkgTmd1eWVuIGV0IGFsLiwgY2xlYW5lZCB0aGUgZGF0YSwgcGVyZm9ybWVkIG5vcm1hbGl6YXRpb24sIGdlbmUgaWRlbnRpZmllciBtYXBwaW5nLCBhbmQgc29tZSBhZGRpdGlvbmFsIHByZWxpbWluYXJ5IGFuYWx5c2lzIG9uIHRoZSBkYXRhc2V0LiBBcyBzdWdnZXN0ZWQgYnkgdGhlIHRpdGxlLCB0aGUgYXV0aG9ycyBvZiB0aGUgcHVibGljYXRpb24gc3R1ZGllZCB0aGUgZWZmZWN0cyBvZiBjYW5uYWJpZGlvbCBvbiBTQVJTLUNvVi0yIHJlcGxpY2F0aW9uIGFuZCBob3N0J3MgaW1tdW5lIHJlc3BvbnNlLiBUaGUgYXV0aG9ycyBoeXBvdGhlc2l6ZWQgYW5kIGNvbmNsdWRlZCB0aGF0IGNhbm5hYmlkaW9sIGluaGliaXRzIFNBUlMtQ29WLTIgcmVwbGljYXRpb24gYnkgdXAtcmVndWxhdGluZyB0aGUgaG9zdCBJUkUxzrEgcmlib251Y2xlYXNlIGVuZG9wbGFzbWljIHJldGljdWx1bSAoRVIpIHN0cmVzcyByZXNwb25zZSBhbmQgaW50ZXJmZXJvbiBzaWduYWxpbmcgcGF0aHdheXMuIFRoZSBleHBlcmltZW50IGNvbmRpdGlvbnMgZnJvbSBpbiB0aGUgZGF0YXNldCBjYW4gYmUgY2F0ZWdvcml6ZWQgaW50bwoKLSBJbmZlY3Rpb24gd2l0aCBTQVJTLUNvVi0yLCBubyB0cmVhdG1lbnQKLSBJbmZlY3Rpb24gd2l0aCBTQVJTLUNvVi0yLCB0cmVhdG1lbnQgd2l0aCBDYW5uYWJpZGlvbAotIE5vIGluZmVjdGlvbiwgbm8gdHJlYXRtZW50Ci0gTm8gaW5mZWN0aW9uLCB0cmVhdG1lbnQgd2l0aCBDYW5uYWJpZGlvbAoKd2l0aCAzIHJlcGxpY2F0ZXMgZm9yIGVhY2ggdGVzdCBjb25kaXRpb24uCgpXZSBvYnRhaW5lZCB0aGUgZGF0YXNldCBmcm9tIEdFTyB3aXRoIElEIEdTRTE2ODc5NywgYXNzb2NpYXRlZCB3aXRoIHRoZSBzdHVkeSAqQ2FubmFiaWRpb2wgaW5oaWJpdHMgU0FSUy1DT1YtMiByZXBsaWNhdGlvbiBhbmQgcHJvbW90ZXMgdGhlIGhvc3QgaW5uYXRlIGltbXVuZSByZXNwb25zZSogcHVibGlzaGVkIG9uIFNjaWVuY2UgQWR2YW5jZXMuIE91dCBvZiB0aGUgdG90YWwgb2YgNTc4MzIgZ2VuZXMsIDEzNzA1IHJlbWFpbmVkIGFmdGVyIHJlbW92aW5nIGxvdyBjb3VudHMgYW5kIGdlbmVzIHdpdGggZHVwbGljYXRlIGlkZW50aWZpZXJzLgoKRGF0YSBub3JtYWxpemF0aW9uIGlzIHBlcmZvcm1lZCB1c2luZyBUTU0gd2l0aCB0aGUgZWRnZVIgcGFja2FnZS4gTm9ybWFsaXphdGlvbiBjb3JyZWN0cyB0aGUgbGFyZ2UgZGV2aWF0aW9uIG9mIHRoZSBtZWFucyBvZiB1bnRyZWF0ZWQgZ3JvdXBzIGZyb20gdGhlIGdyb3VwcyB0cmVhdGVkIHdpdGggQ0JELCB3aGlsZSBzdGlsbCBwcmVzZXJ2aW5nIHNvbWUgb2YgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgb3JpZ2luYWwgc2FtcGxlIGRpc3RyaWJ1dGlvbi4KCmBgYHtyIG5vcm1hbGl6ZWRfZGF0YV9wbG90LCBtZXNzYWdlPUZBTFNFfQpjb3VudHNfZGVuc2l0eV9ub3JtYWxpemVkIDwtIGFwcGx5KGxvZzIobm9ybWFsaXplZF9jb3VudHMpLCAyLCBkZW5zaXR5KQoKeGxpbSA8LSAwOyB5bGltIDwtIDAKZm9yIChpIGluIDE6bGVuZ3RoKGNvdW50c19kZW5zaXR5X25vcm1hbGl6ZWQpKSB7CiAgeGxpbSA8LSByYW5nZShjKHhsaW0sIGNvdW50c19kZW5zaXR5X25vcm1hbGl6ZWRbW2ldXSR4KSk7CiAgeWxpbSA8LSByYW5nZShjKHlsaW0sIGNvdW50c19kZW5zaXR5X25vcm1hbGl6ZWRbW2ldXSR5KSkKfQpjb2xzIDwtIHJhaW5ib3cobGVuZ3RoKGNvdW50c19kZW5zaXR5X25vcm1hbGl6ZWQpKQpsdHlzIDwtIHJlcCgxLCBsZW5ndGgoY291bnRzX2RlbnNpdHlfbm9ybWFsaXplZCkpCgpwbG90KGNvdW50c19kZW5zaXR5X25vcm1hbGl6ZWRbWzFdXSwgeGxpbSA9IHhsaW0sIHlsaW0gPSB5bGltLCB0eXBlID0gIm4iLCB5bGFiID0gIlNtb290aGluZyBkZW5zaXR5IG9mIGxvZzItQ1BNIiwgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgY291bnQgZGVuc2l0eSBmb3IgZWFjaCBleHBlcmltZW50IGNvbmRpdGlvbiAobm9ybWFsaXplZCkiLCBjZXgubGFiID0gMC44NSkKZm9yIChpIGluIDE6bGVuZ3RoKGNvdW50c19kZW5zaXR5X25vcm1hbGl6ZWQpKQogIGxpbmVzKGNvdW50c19kZW5zaXR5X25vcm1hbGl6ZWRbW2ldXSwgY29sID0gY29sc1tpXSwgbHR5ID0gbHR5c1tpXSkKbGVnZW5kKCJ0b3ByaWdodCIsIGNvbG5hbWVzKGRhdGEycGxvdCksIGNvbD1jb2xzLCBsdHk9bHR5cywgY2V4PTAuNzUsIGJvcmRlcj0iYmx1ZSIsIHRleHQuY29sID0gImdyZWVuNCIsIG1lcmdlID0gVFJVRSwgYmcgPSAiZ3JheTkwIikKYGBgCgpXZSBvYnNlcnZlIHNlcGFyYXRpb24gb2YgZ3JvdXBzIGFmdGVyIG5vcm1hbGl6YXRpb24uCgpgYGB7ciBhMV9tZHMsIG1lc3NhZ2U9RkFMU0V9CmxpbW1hOjpwbG90TURTKGxvZzIobm9ybWFsaXplZF9jb3VudHMpLCBsYWJlbHM9cm93bmFtZXMoc2FtcGxlcyksIGNvbCA9IGMoImRhcmtncmVlbiIsImJsdWUiKVtmYWN0b3Ioc2FtcGxlcyRjYmRfdHJlYXRtZW50KV0sIG1haW4gPSAiTURTIHBsb3QgYWZ0ZXIgbm9ybWFsaXphdGlvbiBzaG93aW5nIGRpc3RhbmNlcyBiZXR3ZWVuIHNhbXBsZXMiKQpsZWdlbmQoInRvcGxlZnQiLCBsZWdlbmQ9cm93bmFtZXMoc2FtcGxlcyksIGZpbGw9YygiZGFya2dyZWVuIiwiYmx1ZSIpW2ZhY3RvcihzYW1wbGVzJGNiZF90cmVhdG1lbnQpXSkKYGBgCgpXZSB3aWxsIHJldmlzaXQgdGhpcyBmaWd1cmUgbGF0ZXIgd2hlbiBwZXJmb3JtaW5nIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzaXMuCgpJZGVudGlmaWVyIG1hcHBpbmcgd2FzIHBlcmZvcm1lZCB1c2luZyB0aGUgcGFja2FnZSBiaW9tYVJ0IHdpdGggZGF0YSBmcm9tIEVuc2VtYmwuIFRoZSBFbnNlbWJsIGdlbmUgSURzIGluIHRoZSBvcmlnaW5hbCBkYXRhc2V0IGFyZSBtYXBwZWQgdG8gdGhlIGNvcnJlc3BvbmRpbmcgSFVHTyBnZW5lIHN5bWJvbHMuIFRoZSBmaW5hbCBkYXRhc2V0IGluY2x1ZGVkIDEzNzA1IGdlbmVzIHdpdGggdW5pcXVlIGlkZW50aWZpZXJzLgoKIyBTZXR1cAoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBpbXBvcnQgYW5kIGluc3RhbGwgdGhlIG5lY2Vzc2FyeSBwYWNrYWdlcyBmb3IgdGhpcyBhc3NpZ25tZW50LCBpbiB3aGljaCB3ZSB3aWxsIGNvbmR1Y3QgYSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyB1c2luZyB0aGUgbm9ybWFsaXplZCBkYXRhc2V0IGFuZCBhIHRocmVzaG9sZGVkIG92ZXItcmVwcmVzZW50YXRpb24gYW5hbHlzaXMuCgpgYGB7ciBhMl9wYWNrYWdlcywgbWVzc2FnZT1GQUxTRX0KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJtYWdyaXR0ciIpKQogIGluc3RhbGwucGFja2FnZXMoIm1hZ3JpdHRyIikKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJjaXJjbGl6ZSIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIGluc3RhbGwucGFja2FnZXMoImNpcmNsaXplIikKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJDb21wbGV4SGVhdG1hcCIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJDb21wbGV4SGVhdG1hcCIpCmlmICghcmVxdWlyZU5hbWVzcGFjZSgiZ3Byb2ZpbGVyMiIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJncHJvZmlsZXIyIikKCiMgZWRnZVIgYW5kIG90aGVyIHBhY2thZ2VzIGFyZSBpbXBvcnRlZCBpbiBBMSwgd2hpY2ggaXMgc291cmNlZCBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBub3RlYm9vawoKbGlicmFyeShtYWdyaXR0cikKCmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlID0gRkFMU0UpCmBgYAoKIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBBbmFseXNpcyB1c2luZyBlZGdlUgoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBhcmUgZ29pbmcgdG8gcGVyZm9ybSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcy4KCkxldCB1cyBmaXJzdCBleGFtaW5lIHRoZSBub3JtYWxpemVkIGRhdGEuCgpgYGB7ciBub3JtYWxpemVkX2RhdGEsIG1lc3NhZ2U9RkFMU0V9CmtuaXRyOjprYWJsZShkWzE6MTAsMTo2XSRjb3VudHMsIGNhcHRpb24gPSAiTm9ybWFsaXplZCBnZW5lIGNvdW50cyIpICU+JSBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKCJzdHJpcGVkIikKa25pdHI6OmthYmxlKGRbMToxMCwxOjZdJHNhbXBsZXMsIGNhcHRpb24gPSAiU2FtcGxlIGdyb3VwcyIpICU+JSBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKCJzdHJpcGVkIikKYGBgCiMjIFdvcmtmbG93CgpXZSB3aWxsIGJlIGNhbGN1bGF0aW5nIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGZvbGxvd2luZyB0aGlzIGdlbmVyYWwgd29ya2Zsb3c6CgoxLiBDcmVhdGUgZGVpc25nIG1hdHJpeC4gV2Ugd2lsbCBlbnN1cmUgdGhhdCBhbGwgZmFjdG9ycyB0aGF0IGNvdWxkIGNvbnRyaWJ1dGUgdG8gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gaXMgYWNjb3VudGVkIGZvciBpbiB0aGUgZGVzaWduIG1hdHJpeC4KCjIuIFVzZSBhIG1lYW4tdmFyaWFuY2UgcGxvdCB0byBjb25maXJtIHRoYXQgdGhlIGRhdGEgZm9sbG93cyB0aGUgbmVnYXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uIGFzc3VtcHRpb24gZm9yIHVzaW5nIHRoZSBxdWFzaS1saWtlbGlob29kIG1vZGVsIGluIGVkZ2VSLgoKMy4gRml0IG91ciBkYXRhIHRvIHRoZSBtb2RlbCBhbmQgY2FsY3VsYXRlZCBwLXZhbHVlIGFuZCBjb3JyZWN0ZWQgcC12YWx1ZS4KCjQuIFVzZSBhIHRocmVzaG9sZCB0byBleHRyYWN0IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyB3aXRoIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZXMuCgo1LiBQbG90IGFuZCB2aXN1YWxpemUuCgojIyBDcmVhdGluZyB0aGUgRGVzaWduIE1hdHJpeAoKSW4gb3JkZXIgdG8gcGVyZm9ybSBzdGF0aXN0aWNhbCB0ZXN0aW5nLCB3ZSBuZWVkIGEgZGVzaWduIG1hdHJpeCB0aGUgZGVmaW5lcyBvdXIgbW9kZWwuIE5vdGljZSB0aGF0IGluIG91ciBkYXRhc2V0LCB0aGVyZSBhcmUgdGhyZWUgZmFjdG9yczogCgoxLiBUcmVhdG1lbnQgd2l0aCBDQkQKMi4gSW5mZWN0aW9uIHN0YXR1cyAoaW5mZWN0ZWQgb3Igbm90KQozLiBQYXRpZW50CgpIZW5jZSwgaWRlYWxseSwgd2Ugd291bGQgbGlrZSB0byBhY2NvdW50IGZvciBhbGwgdGhyZWUgZmFjdG9ycyBpbiBvdXIgZGVzaWduIG1hdHJpeC4KCmBgYHtyIGRlc2lnbl9tYXRyaXgsIG1lc3NhZ2U9RkFMU0V9Cm1vZGVsX2Rlc2lnbiA8LSBtb2RlbC5tYXRyaXgofiBzYW1wbGVzJHBhdGllbnQgKyBzYW1wbGVzJGNiZF90cmVhdG1lbnQgKyBzYW1wbGVzJGluZmVjdGVkKQptb2RlbF9kZXNpZ25bLDRdIDwtICFtb2RlbF9kZXNpZ25bLDRdCmNvbG5hbWVzKG1vZGVsX2Rlc2lnbilbNF0gPC0gInNhbXBsZXMkY2JkX3RyZWF0bWVudENCRCIKa25pdHI6OmthYmxlKG1vZGVsX2Rlc2lnbiwgY2FwdGlvbiA9ICJEZXNpZ24gbWF0cml4IikgJT4lCiAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZygic3RyaXBlZCIpCmBgYAoKIyMgRGlzdHJpYnV0aW9uIG9mIERhdGEKCkZvciBvdXIgZG93bnN0cmVhbSBhbmFseXNpcywgd2UgYXJlIGdvaW5nIHRvIHVzZSBlZGdlUi4gV2UgY2hvc2UgZWRnZVIgYmVjYXVzZSBpdCBpcyBzcGVjaWZpY2FsbHkgZGVzaWduZWQgZm9yIFJOQVNlcSBkYXRhLiBIb3dldmVyLCBvbmUgaW1wb3J0YW50IHVuZGVybHlpbmcgYXNzdW1wdGlvbiBmb3IgdXNpbmcgdGhlIHF1YXNpLWxpa2VsaWhvb2QgbW9kZWwgaXMgdGhhdCB0aGUgZGF0YSBmb2xsb3dzIGEgbmVnYXRpdmUgYmlub21pYWwgZGlzdHJpYnV0aW9uLiBXZSBuZWVkIHRvIHZlcmlmeSB0aGF0IG91ciBkYXRhc2V0IGluZGVlZCBtZWV0cyB0aGF0IGFzc3VtcHRpb24uCgpMZXQgdXMgdGhlIGNhbGN1bGF0ZSB0aGUgZGlzcGVyc2lvbiBhbmQgcGxvdCB0byB2aXN1YWxpemUgdGhlIG1lYW4tdmFyaWFuY2UgcmVsYXRpb25zaGlwLgoKYGBge3IgZGlzcGVyc2lvbl9jYWxjLCBtZXNzYWdlPUZBTFNFfQpkIDwtIGVkZ2VSOjpER0VMaXN0KGNvdW50cyA9IG5vcm1hbGl6ZWRfY291bnRzX2Fubm90WywzOm5jb2wobm9ybWFsaXplZF9jb3VudHNfYW5ub3QpXSwgZ3JvdXAgPSBzYW1wbGVzJGNiZF90cmVhdG1lbnQpCmQgPC0gZWRnZVI6OmVzdGltYXRlRGlzcChkLCBtb2RlbF9kZXNpZ24pCmBgYAoKYGBge3IgbWVhbl92YXJfcGxvdH0KZWRnZVI6OnBsb3RNZWFuVmFyKGQsCiAgICAgICAgICAgICAgICAgICBzaG93LnJhdy52YXJzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgIHNob3cudGFnd2lzZS52YXJzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgIE5CbGluZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICBzaG93LmF2ZS5yYXcudmFycyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICBzaG93LmJpbm5lZC5jb21tb24uZGlzcC52YXJzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgIG1haW4gPSAiTWVhbi1WYXJpYW5jZSBQbG90IGZvciBWYXJpYW5jZSBhbmQgRGlzcGVyc2lvbiBvZiBPdXIgRGF0YSIpCiMgZGlzcGxheSBsZWdlbmQKbGVnZW5kKCJ0b3BsZWZ0IiwgCiAgICAgICBsZWdlbmQ9YygiUmF3IERhdGEiLCAiVGFnd2lzZSBEaXNwZXJzaW9uIiwgIkF2ZXJhZ2UgUmF3IFZhcmlhbmNlcyIsIAogICAgICAgICAgICAgICAgIkJpbm5lZCBDb21tb24gRGlzcGVyc2lvbiIsICJOZWdhdGl2ZSBCaW5vbWlhbCBMaW5lIiksIAogICAgICAgY29sID0gYygiZ3JleSIsICJsaWdodGJsdWUiLCAibWFyb29uIiwgInJlZCIsICJkb2RnZXJibHVlMiIpLCBwY2g9YygxLDEsNCw0LE5BKSwgbHR5PWMoMCwwLDAsMCwxKSwgbHdkPWMoMSwxLDEsMSwyKSwgY2V4PTAuNikKYGBgCgpBcyBkZW1vbnN0cmF0ZWQgYnkgdGhlIG1lYW4tdmFyaWFuY2UgcGxvdCBhYm92ZSwgd2UgY2FuIHNlZSB0aGF0IHRoZSBkaXNwZXJzaW9uIGFuZCB2YXJpYW5jZSBvZiBvdXIgZGF0YSBpbmRlZWQgcm91Z2hseSBmb2xsb3dzIHRoZSBuZWdhdGl2ZSBiaW5vbWlhbCBkaXN0cmlidXRpb24uCgojIyBBbmFseXNpcyBVc2luZyBlZGdlUgoKTm93LCB3ZSBoYXZlIGNyZWF0ZWQgdGhlIGRlc2lnbiBtYXRyaXggYW5kIHZlcmlmaWVkIHRoZSBhc3N1bXB0aW9uIGZvciB0aGUgZGF0YSB0byBiZSBuZWdhdGl2ZS1iaW5vbWlhbGx5IGRpc3RyaWJ1dGVkLCB3ZSBjYW4gcHJvY2VlZCB0byB0aGUgbmV4dCBzdGFnZSBvZiBvdXIgYW5hbHlzaXMgYW5kIHBlcmZvcm0gc3RhdGlzdGljYWwgdGVzdGluZyBhbmQgY29ycmVjdGlvbnMgdG8gZW5zdXJlIHRoYXQgd2Ugb25seSBnZXQgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuIFdlIHVzZWQgdGhlIHF1YXNpLWxpa2VsaWhvb2QgbW9kZWxzIHNpbmNlIG91ciBkYXRhc2V0IGlzIGZyb20gYW4gUk5BU2VxIGV4cGVyaW1lbnQgYW5kIHF1YXNpLWxpa2VsaWhvb2QgbW9kZWxzIGFyZSBiZXN0IHN1aXRlZCB0byBoYW5kbGUgUk5BU2VxIGRhdGEuCgpgYGB7ciBmaXRfcWwsIG1lc3NhZ2U9RkFMU0V9CmZpdCA8LSBlZGdlUjo6Z2xtUUxGaXQoZCwgbW9kZWxfZGVzaWduKQpgYGAKCk9uY2Ugd2UgaGF2ZSBmaXQgdGhlIG1vZGVsLCB3ZSBjYW4gcHJvY2VlZCB0byBjYWxjdWxhdGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24uIFJlY2FsbCB0aGF0IG91ciBnb2FsIGlzIHRvIHZlcmlmeSB0aGUgcm9sZSBvZiBjYW5uYWJpZGlvbCBpbiBhZmZlY3RpbmcgdGhlIHJlcGxpY2F0aW9uIGFiaWxpdHkgb2YgU0FSUy1Db1YtMiwgc28gd2Ugd2lsbCBiZSB1c2luZyBgY2JkX3RyZWF0bWVudGAgYXMgdGhlIGNvbnRyYXN0LgoKYGBge3IgdGVzdF9xbCwgbWVzc2FnZT1GQUxTRX0KcWxmIDwtIGVkZ2VSOjpnbG1RTEZUZXN0KGZpdCwgY29lZiA9ICdzYW1wbGVzJGNiZF90cmVhdG1lbnRDQkQnKQpxbGZfZGlmZl9leHAgPC0gZWRnZVI6OnRvcFRhZ3MocWxmLCBzb3J0LmJ5ID0gIlBWYWx1ZSIsIG4gPSBucm93KG5vcm1hbGl6ZWRfY291bnRzX2Fubm90KSkKa25pdHI6OmthYmxlKHFsZl9kaWZmX2V4cFsxOjEwLF0kdGFibGUsIHR5cGU9Imh0bWwiLCBkaWdpdHMgPSAyMCwgY2FwdGlvbiA9ICJUb3AgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIikgJT4lCiAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZygic3RyaXBlZCIpCmBgYAoKV2UgY2FuIGV4YW1pbmUgdGhlIG51bWJlciBvZiBnZW5lcyBwYXNzIHRoZSB0aHJlc2hvbGQgYW5kIGNvcnJlY3Rpb24uIFdlIGFyZSB1c2luZyAwLjA1IGFzIHRoZSB0aHJlc2hvbGQgZm9yIHAtdmFsdWUgYXMgaXQgaXMgY29tbW9ubHkgdXNlZCBpbiBwcmFjdGljZS4KCmBgYHtyIGNvdW50X2RpZmZfcHZhbCwgbWVzc2FnZT1GQUxTRX0KIyBudW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzZWQgdGhlIHRocmVzaG9sZApzdW0ocWxmX2RpZmZfZXhwJHRhYmxlJFBWYWx1ZSA8IDAuMDUpCmBgYApgYGB7ciBjb3VudF9kaWZmX2ZkciwgbWVzc2FnZT1GQUxTRX0KIyBudW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzZWQgY29ycmVjdGlvbgpzdW0ocWxmX2RpZmZfZXhwJHRhYmxlJEZEUiA8IDAuMDUpCmBgYAoKVGhlIHRocmVzaG9sZCBvZiAwLjA1IGdpdmVzIHVzIHF1aXRlIGEgbG90IG9mIGdlbmVzLCBpbiBvcmRlciB0byBnZXQgbW9yZSBtZWFuaW5nZnVsIGhpdHMgaW4gdGhlIGRvd25zdHJlYW0gZ2VuZSBlbnJpY2htZW50IGFuYWx5c2lzLCB3ZSB3aWxsIG1ha2UgdGhlIHRoZXJzaG9sZCBtb3JlIHN0cmluZ2VudC4KCmBgYHtyIGNvdW50X2RpZmZfcHZhbF9zdHJpbmdlbnQsIG1lc3NhZ2U9RkFMU0V9CiMgbnVtYmVyIG9mIGdlbmVzIHRoYXQgcGFzc2VkIHRoZSB0aHJlc2hvbGQKc3VtKHFsZl9kaWZmX2V4cCR0YWJsZSRQVmFsdWUgPCAwLjAxICYgYWJzKHFsZl9kaWZmX2V4cCR0YWJsZSRsb2dGQykgPiAxLjUpCmBgYApgYGB7ciBjb3VudF9mZHJfc3RyaW5nZW50LCBtZXNzYWdlPUZBTFNFfQojIG51bWJlciBvZiBnZW5lcyB0aGF0IHBhc3NlZCBjb3JyZWN0aW9uCnN1bShxbGZfZGlmZl9leHAkdGFibGUkRkRSIDwgMC4wMSAmIGFicyhxbGZfZGlmZl9leHAkdGFibGUkbG9nRkMpID4gMS41KQpgYGAKCiMjIFZpc3VhbGl6YXRpb24gb2YgRGlmZmVyZW50aWFsbHkgRXhwcmVzc2VkIEdlbmVzCgpOb3cgd2UgY2FuIHJldHJpZXZlIHRoZSBsaXN0IG9mIGRpZmZlcmVudGlhbCBleHByZXNzZWQgZ2VuZXMgYW5kIHZpc3VhbGl6ZSB1c2luZyBkaWZmZXJlbnQgcGxvdHMuIFdlIHdpbGwgZmlyc3QgcGxvdCB0aGVtIG9uIGEgdm9sY2FubyBwbG90LiBFYWNoIGdlbmUgaXMgcmVwcmVzZW50ZWQgYnkgYSBwb2ludCBpbiB0aGUgcGxvdC4gVGhlIGhvcml6b250YWwgYXhpcyBvZiB0aGUgcGxvdCBpcyB0aGUgJFxsb2dfMiQgZm9sZCBjaGFuZ2UgYW5kIHRoZSB2ZXJ0aWNhbCBheGlzIGlzIHRoZSAkLVxsb2dfezEwfXAkIHdoaWNoIGluZGljYXRlcyB0aGUgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIG9mIGVhY2ggZ2VuZSAoaG93IGxpa2VseSB0aGUgZGlmZmVyZW50aWFsIGlzIGR1ZSB0byBhY3R1YWwgYmlvbG9naWNhbCB2YXJpYXRpb24pLgoKYGBge3Igdm9sY2Fub19wbG90LCBtZXNzYWdlPUZBTFNFfQp2b2xjYW5vX2NvbG9yX3BhbGV0dGUgPSByZXAoJ2dyYXknLCB0aW1lcyA9IG5yb3cocWxmX2RpZmZfZXhwJHRhYmxlKSkKdm9sY2Fub19jb2xvcl9wYWxldHRlW3FsZl9kaWZmX2V4cCR0YWJsZSRsb2dGQyA8IDAgJiBxbGZfZGlmZl9leHAkdGFibGUkRkRSIDwgMC4wMSAmIGFicyhxbGZfZGlmZl9leHAkdGFibGUkbG9nRkMpID4gMS41XSA8LSAnYmx1ZScKdm9sY2Fub19jb2xvcl9wYWxldHRlW3FsZl9kaWZmX2V4cCR0YWJsZSRsb2dGQyA+IDAgJiBxbGZfZGlmZl9leHAkdGFibGUkRkRSIDwgMC4wMSAmIGFicyhxbGZfZGlmZl9leHAkdGFibGUkbG9nRkMpID4gMS41XSA8LSAncmVkJwoKcGxvdChxbGZfZGlmZl9leHAkdGFibGUkbG9nRkMsIAogICAgIC1sb2cocWxmX2RpZmZfZXhwJHRhYmxlJFBWYWx1ZSwgYmFzZT0xMCksIAogICAgIGNvbCA9IHZvbGNhbm9fY29sb3JfcGFsZXR0ZSwKICAgICB4bGFiID0gImxvZzIgZm9sZCBjaGFuZ2UiLAogICAgIHlsYWIgPSAiLWxvZzEwIHAiLAogICAgIG1haW4gPSAiVm9sY2FubyBwbG90IHNob3dpbmcgdXByZWd1bGF0ZWQgYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZXMiCiAgICApCgpsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kPWMoIkRvd25yZWd1bGF0ZWQgaW4gQ0JEIHRyZWF0ZWQgY2VsbHMiLCJVcHJlZ3VsYXRlZCBpbiBDQkQgdHJlYXRlZCBjZWxscyIsICJOb3Qgc2lnbmlmaWNhbnQiKSxmaWxsID0gYygiYmx1ZSIsICJyZWQiLCAiZ3JleSIpLCBjZXggPSAwLjUpCmBgYAoKTmV4dCwgd2Ugd2lsbCB2aXN1YWxpemUgdGhlIHVwcmVndWxhdGVkIGFuZCBkb3ducmVndWxhdGVkIGdlbmVzIGFjcm9zcyBkaWZmZXJlbnQgdGVzdCBjb25kaXRpb25zIHVzaW5nIGEgaGVhdG1hcC4KCmBgYHtyIGhlYXRtYXAsIG1lc3NhZ2U9RkFMU0V9CmRpZmZfZXhwX2xzdCA8LSBxbGZfZGlmZl9leHAkdGFibGVbcWxmX2RpZmZfZXhwJHRhYmxlJEZEUiA8IDAuMDEgJiBhYnMocWxmX2RpZmZfZXhwJHRhYmxlJGxvZ0ZDKSxdCmRpZmZfZXhwX2xzdCRoZ25jX3N5bWJvbCA8LSByb3duYW1lcyhkaWZmX2V4cF9sc3QpCgojIG5vcm1hbGl6ZWQgY291bnRzIG9mIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcwptYXRfY291bnRfbm9ybWFsaXplZF9kaWZmIDwtIG5vcm1hbGl6ZWRfY291bnRzX2Fubm90W2RpZmZfZXhwX2xzdCRoZ25jX3N5bWJvbCwgMzpuY29sKG5vcm1hbGl6ZWRfY291bnRzX2Fubm90KV0KIyByZXNjYWxlIHRoZSBjb3VudHMKbWF0X2NvdW50X25vcm1hbGl6ZWRfZGlmZiA8LSB0KHNjYWxlKHQobWF0X2NvdW50X25vcm1hbGl6ZWRfZGlmZikpKQpoZWF0bWFwX2NvbG9yX3BhbGV0dGUgPC0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYyhtaW4obWF0X2NvdW50X25vcm1hbGl6ZWRfZGlmZiksIDAsIG1heChtYXRfY291bnRfbm9ybWFsaXplZF9kaWZmKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKQpDb21wbGV4SGVhdG1hcDo6SGVhdG1hcChhcy5tYXRyaXgobWF0X2NvdW50X25vcm1hbGl6ZWRfZGlmZiksCiAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAic2NhbGVkIG5vcm1hbGl6ZWQgY291bnQiLAogICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHVtbnMgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfZGVuZCA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fZGVuZCA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gaGVhdG1hcF9jb2xvcl9wYWxldHRlLCAKICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdGl0bGUgPSAiU2FtcGxlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgIHJvd190aXRsZSA9ICJHZW5lcyIsCiAgICAgICAgICAgICAgICAgICAgICAgIHVzZV9yYXN0ZXIgPSBUUlVFKQpgYGAKCiMjIERpc2N1c3Npb24KCjEuIEluaXRpYWxseSwgSSB1c2VkIHRoZSBwLXZhbHVlIG9mIDAuMDUgYXMgaXQgaXMgd2lkZWx5IHVzZWQgaW4gcHJhY3RpY2UuIFRoaXMgZ2l2ZXMgdXMgNTUwMSBnZW5lcyBwcmlvciB0byBjb3JyZWN0aW9uLiBUaGlzIGlzIHF1aXRlIGEgbGFyZ2UgbnVtYmVyIG9mIGdlbmVzLCBzbyB3ZSBjaGFuZ2VkIHRoZSBwLXZhbHVlIHRocmVzaG9sZCB0byAwLjAxIHRvIGxpbWl0IHRoZSBudW1iZXIgb2YgZ2VuZXMgaW5jbHVkZWQuIFdlIGZ1cnRoZXIgYWRkZWQgdGhlIGNyaXRlcmlhIHRoYXQgYSBnZW5lIG11c3QgaGF2ZSBhbiBhYnNvbHV0ZSBsb2cgZm9sZCBjaGFuZ2UgZ3JlYXRlciB0aGFuIDEuNS4gQnkgZG9pbmcgc28sIHdlIGFyZSBjb250cmFpbmluZyBvdXJzZWx2ZXMgdG8gb25seSBnZXQgdGhlIGdlbmVzIHRoYXQgYXJlIGhpZ2hseSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgd2l0aCBoaWdoIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZS4KCjIuIEZvciBjb3JyZWN0aW9uLCB3ZSB1c2VkIEJlbmphbWluaS1Ib2NoYmVyZy4gVGhlIHR3byBtYWluIG1ldGhvZHMgZGlzY3Vzc2VkIGZvciBjb3JyZWN0aW5nIGZhbWlseS13aXNlIGZhbHNlIGRpc2NvdmVyeSByYXRlIGFyZSBCb25mZXJyb25pIGFuZCBCZW5qYW1pbmktSG9jaGJlcmcgY29ycmVjdGlvbi4gV2Ugd2FudCB0byBnZXQgbWVhbmluZ2Z1bCBoaXRzIHdpdGhvdXQgZXhjbHVkaW5nIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgb25lcy4gQm9uZmVycm9uaSdzIG1ldGhvZCBpcyBvdmVybHkgc3RyaW5nZW50IHNvIGl0IGlzIG5vdCBxdWl0ZSBzdWl0YWJsZSBmb3Igb3VyIHB1cnBvc2VzLiBVc2luZyBCZW5qYW1pbmktSG9jaGJlcmcgd2lsbCBnaXZlIHVzIGEgcmljaGVyIHNldCBvZiBnZW5lcyB0aGF0IHdlIGNhbiB1c2UgZm9yIGRvd25zdHJlYW0gYW5hbHlzaXMuCgozLiBWb2xjYW5vIHBsb3Qgc2hvd24gYWJvdmUuIE5vdGUgdGhhdCB3ZSBhbHNvIGFwcGxpZWQgdGhlIHJlc3RyaWN0aW9uIHRoYXQgb25seSBnZW5lcyB3aXRoIGFib3NsdXRlIGxvZyBmb2xkIGNoYW5nZSBncmVhdGVyIHRoYW4gMS41IGFyZSB0byBiZSBpbmNsdWRlZC4KCjQuIFRoZXJlIGlzIHNpZ25pZmljYW50IGNsdXN0ZXJpbmcgd2l0aGluIGNvbmRpdGlvbnMuIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGUgdGVzdCBjb25kaXRpb24gKENCRCB2LnMuIG5vIENCRCkgZG9lcyBoYXZlIGEgc2lnbmlmaWNhbnQgZWZmZWN0IG9uIGdlbmUgZXhwcmVzc2lvbnMuCgojIFRocmVzaG9sZGVkIE92ZXJyZXByZXNlbnRhdGlvbiBBbmFseXNpcyB1c2luZyBnOlByb2ZpbGVyCgpGb3IgdGhlIGZpbmFsIHBhcnQgb2YgdGhpcyBhc3NpZ25tZW50LCB3ZSB3aWxsIHBlcmZvcm0gYSB0aHJlc2hvbGRlZCBvdmVycmVwcmVzZW50YXRpb24gYW5hbHlzaXMgdXNpbmcgZzpQcm9maWxlci4gSW4gdGhlIHByZXZpb3VzIHNlY3Rpb24sIHdlIGhhdmUgY29tcGlsZWQgYSBsaXN0IG9mIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcy4gSGVyZSwgd2Ugd2FudCB0byBmdXJ0aGVyIGRpdmlkZSB0aGVtIGludG8gdXByZWd1bGF0ZWQgYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZXMuCgpgYGB7ciBzcGxpdF91cGRvd25yZWd1bGF0ZWR9CnVwcmVndWxhdGVkX2dlbmVfbHN0IDwtIGRpZmZfZXhwX2xzdFtkaWZmX2V4cF9sc3QkbG9nRkMgPiAwLF0KZG93bnJlZ3VsYXRlZF9nZW5lX2xzdCA8LSBkaWZmX2V4cF9sc3RbZGlmZl9leHBfbHN0JGxvZ0ZDIDwgMCxdCmBgYAoKV2UgdXNlIHRoZSBSIHBhY2thZ2UgZm9yIGc6UHJvZmlsZXIgdG8gcGVyZm9ybSB0aGUgZ2VuZSBlbnJpY2htZW50IGFuYWx5c2lzLiBGb3IgY29ycmVjdGlvbiwgd2UgdXNlZCBGRFIgYXMgaXQgaXMgbGVzcyBzdHJpbmdlbnQgdGhhbiBCb25mZXJyb25pIGFuZCBpcyBpbnRyb2R1Y2VkIGFzIHRoZSBwcmVmZXJyZWQgY29ycmVjdGlvbiBtZXRob2QgaW4gY2xhc3MuIFdlIHVzZWQgR08gQmlvbG9naWNhbCBQcm9jZXNzLCBHTyBNb2xlY3VsYXIgRnVuY3Rpb24sIGFuZCBLRUdHIGFzIHRob3NlIGFyZSB0aGUgb25lcyB1c2VkIGJ5IHRoZSBhdXRob3Igb2YgdGhlIG9yaWdpbmFsIHB1YmxpY2F0aW9uLiBGb3IgYSBtb3JlIGRldGFpbGVkIG92ZXJ2aWV3IG9mIHRoZSB3b3JrZmxvdywgcGxlYXNlIHJlZmVyIHRvIHRoZSBEaXNjdXNzaW9uIHN1YnNlY3Rpb24gbG9jYXRlZCBhdCB0aGUgZW5kIG9mIHRoaXMgc2VjdGlvbi4KCiMjIFVwcmVndWxhdGVkIEdlbmVzCgpgYGB7ciBncHJvZmlsZXJfdXAsIG1lc3NhZ2U9RkFMU0V9CnVwX3RvcF90ZXJtc19hbGwgPC0gZ3Byb2ZpbGVyMjo6Z29zdChxdWVyeSA9IHJvd25hbWVzKHVwcmVndWxhdGVkX2dlbmVfbHN0KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmdhbmlzbSA9ICJoc2FwaWVucyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjbHVkZV9pZWEgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29ycmVjdGlvbl9tZXRob2QgPSAiZmRyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZXMgPSBjKCJHTzpCUCIsICJHTzpNRiIsICJLRUdHIikpCgp1cF90b3BfdGVybXMgPC0gZGF0YS5mcmFtZSgKICB0ZXJtX25hbWUgPSB1cF90b3BfdGVybXNfYWxsJHJlc3VsdCR0ZXJtX25hbWVbdXBfdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9zaXplIDwgNTAwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cF90b3BfdGVybXNfYWxsJHJlc3VsdCR0ZXJtX3NpemUgPiAyXSwKICB0ZXJtX2lkID0gdXBfdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9pZFt1cF90b3BfdGVybXNfYWxsJHJlc3VsdCR0ZXJtX3NpemUgPCA1MDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXBfdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9zaXplID4gMl0sCiAgc291cmNlID0gdXBfdG9wX3Rlcm1zX2FsbCRyZXN1bHQkc291cmNlW3VwX3RvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fc2l6ZSA8IDUwMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXBfdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9zaXplID4gMl0KKQoKa25pdHI6OmthYmxlKHVwX3RvcF90ZXJtc1sxOjEwLF0sIGNhcHRpb24gPSAiVG9wIGdlbmVzZXRzIHVzaW5nIGxpc3Qgb2YgdXByZWd1bGF0ZWQgZ2VuZXMiKSAlPiUga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZygic3RyaXBlZCIpCmBgYApGb3IgY29udGV4dCwgbGV0J3MgZXhhbWluZSB0aGUgdG9wIHRlcm0gZnJvbSBlYWNoIGRhdGEgc291cmNlLgoKYGBge3IgdXBfdG9wX3Rlcm1fcGVyX2NhdGVnb3J5LCBtZXNzYWdlPUZBTFNFfQprbml0cjo6a2FibGUocmJpbmQodXBfdG9wX3Rlcm1zW3VwX3RvcF90ZXJtcyRzb3VyY2UgPT0gIkdPOkJQIixdWzEsXSwKICAgICAgICAgICAgICAgICAgIHVwX3RvcF90ZXJtc1t1cF90b3BfdGVybXMkc291cmNlID09ICJHTzpNRiIsXVsxLF0sCiAgICAgICAgICAgICAgICAgICB1cF90b3BfdGVybXNbdXBfdG9wX3Rlcm1zJHNvdXJjZSA9PSAiS0VHRyIsXVsxLF0pLAogICAgICAgICAgICAgY2FwdGlvbiA9ICJUb3AgdGVybXMgZnJvbSBlYWNoIGRhdGEgc291cmNlIHVzaW5nIGxpc3Qgb2YgdXByZWd1bGF0ZWQgZ2VuZXMiKSAlPiUKICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKCJzdHJpcGVkIikKYGBgCldlIGNhbiB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0b3AgdGVybXMgZnJvbSBlYWNoIGRhdGEgc291cmNlIHVzaW5nIGFuIE1hbmhhdHRhbiBwbG90LiBUaGlzIGlzIHRoZSBkaXN0cmlidXRpb24gb2YgdGVybXMgcHJpb3IgdG8gcmVtb3ZpbmcgbGFyZ2UgdGVybXMgd2l0aCBvdmVyIDUwMCBnZW5lcy4KCmBgYHtyIHVwX2Rpc3RfcGxvdH0KZ3Byb2ZpbGVyMjo6Z29zdHBsb3QodXBfdG9wX3Rlcm1zX2FsbCkgJT4lIHBsb3RseTo6bGF5b3V0KHRpdGxlID0gIk1hbmhhdHRhbiBwbG90IHNob3dpbmcgZGlzdHJpYnV0aW9uIG9mIHRlcm1zIFxuZnJvbSBlYWNoIGRhdGEgc291cmNlIHVzaW5nIGxpc3Qgb2YgdXByZWd1bGF0ZWQgZ2VuZXMiLCBmb250ID0gbGlzdChzaXplID0gMTApKQpgYGAKCmBgYHtyIGNvdW50X3VwX3RvcF90ZXJtcywgbWVzc2FnZT1GQUxTRX0KbGVuZ3RoKHVwX3RvcF90ZXJtcyR0ZXJtX25hbWUpCmBgYAoKIyMgRG93bnJlZ3VsYXRlZCBHZW5lcwoKV2UgZG8gdGhlIHNhbWUgZm9yIHRoZSBkb3ducmVndWFsdGVkIGdlbmVzLgoKYGBge3IgZ3Byb2ZpbGVyX2Rvd24sIG1lc3NhZ2U9RkFMU0V9CmRvd25fdG9wX3Rlcm1zX2FsbCA8LSBncHJvZmlsZXIyOjpnb3N0KHF1ZXJ5ID0gcm93bmFtZXMoZG93bnJlZ3VsYXRlZF9nZW5lX2xzdCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5pc20gPSAiaHNhcGllbnMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2x1ZGVfaWVhID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcnJlY3Rpb25fbWV0aG9kID0gImZkciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2VzID0gYygiR086QlAiLCAiR086TUYiLCAiS0VHRyIpKQoKZG93bl90b3BfdGVybXMgPC0gZGF0YS5mcmFtZSgKICB0ZXJtX25hbWUgPSBkb3duX3RvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fbmFtZVtkb3duX3RvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fc2l6ZSA8IDUwMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb3duX3RvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fc2l6ZSA+IDJdLAogIHRlcm1faWQgPSBkb3duX3RvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1faWRbZG93bl90b3BfdGVybXNfYWxsJHJlc3VsdCR0ZXJtX3NpemUgPCA1MDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb3duX3RvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fc2l6ZSA+IDJdLAogIHNvdXJjZSA9IGRvd25fdG9wX3Rlcm1zX2FsbCRyZXN1bHQkc291cmNlW2Rvd25fdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9zaXplIDwgNTAwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvd25fdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9zaXplID4gMl0KKQoKa25pdHI6OmthYmxlKGRvd25fdG9wX3Rlcm1zWzE6MTAsXSwgY2FwdGlvbiA9ICJUb3AgZ2VuZXNldHMgdXNpbmcgbGlzdCBvZiBkb3ducmVndWxhdGVkIGdlbmVzIikgJT4lIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoInN0cmlwZWQiKQpgYGAKCmBgYHtyIGRvd25fdG9wX3Rlcm1fcGVyX2NhdGVnb3J5LCBtZXNzYWdlPUZBTFNFfQprbml0cjo6a2FibGUocmJpbmQoZG93bl90b3BfdGVybXNbZG93bl90b3BfdGVybXMkc291cmNlID09ICJHTzpCUCIsXVsxLF0sCiAgICAgICAgICAgICAgICAgICBkb3duX3RvcF90ZXJtc1tkb3duX3RvcF90ZXJtcyRzb3VyY2UgPT0gIkdPOk1GIixdWzEsXSwKICAgICAgICAgICAgICAgICAgIGRvd25fdG9wX3Rlcm1zW2Rvd25fdG9wX3Rlcm1zJHNvdXJjZSA9PSAiS0VHRyIsXVsxLF0pLAogICAgICAgICAgICAgY2FwdGlvbiA9ICJUb3AgdGVybXMgZnJvbSBlYWNoIGRhdGEgc291cmNlIHVzaW5nIGxpc3Qgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcyIpICU+JQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoInN0cmlwZWQiKQpgYGAKUGxvdCB0aGUgTWFuaGF0dGFuIHBsb3Qgc2hvd2luZyBkaXN0cmlidXRpb24gb2YgdGVybXMgZnJvbSBlYWNoIGRhdGEgc291cmNlIHVzaW5nIGxpc3Qgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcy4KCmBgYHtyIGRvd25fZGlzdF9wbG90fQpncHJvZmlsZXIyOjpnb3N0cGxvdChkb3duX3RvcF90ZXJtc19hbGwpICU+JSBwbG90bHk6OmxheW91dCh0aXRsZSA9ICJNYW5oYXR0YW4gcGxvdCBzaG93aW5nIGRpc3RyaWJ1dGlvbiBvZiB0ZXJtc1xuIGZyb20gZWFjaCBkYXRhIHNvdXJjZSB1c2luZyBsaXN0IG9mIGRvd25yZWd1bGF0ZWQgZ2VuZXMiLCBmb250ID0gbGlzdChzaXplID0gMTApKQpgYGAKCmBgYHtyIGNvdW50X2Rvd25fdG9wX3Rlcm1zLCBtZXNzYWdlPUZBTFNFfQpsZW5ndGgoZG93bl90b3BfdGVybXMkdGVybV9uYW1lKQpgYGAKCiMjIEFsbCBEaWZmZXJlbnRpYWxseSBFeHByZXNzZWQgR2VuZXMKCkZpbmFsbHksIGZvciBhbGwgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLgoKYGBge3IgZ3Byb2ZpbGVyX292ZXJhbGwsIG1lc3NhZ2U9RkFMU0V9CnRvcF90ZXJtc19hbGwgPC0gZ3Byb2ZpbGVyMjo6Z29zdChxdWVyeSA9IHJvd25hbWVzKGRpZmZfZXhwX2xzdCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnYW5pc20gPSAiaHNhcGllbnMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4Y2x1ZGVfaWVhID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvcnJlY3Rpb25fbWV0aG9kID0gImZkciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2VzID0gYygiR086QlAiLCAiR086TUYiLCAiS0VHRyIpKQoKdG9wX3Rlcm1zIDwtIGRhdGEuZnJhbWUoCiAgdGVybV9uYW1lID0gdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9uYW1lW3RvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fc2l6ZSA8IDUwMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9zaXplID4gMl0sCiAgdGVybV9pZCA9IHRvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1faWRbdG9wX3Rlcm1zX2FsbCRyZXN1bHQkdGVybV9zaXplIDwgNTAwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fc2l6ZSA+IDJdLAogIHNvdXJjZSA9IHRvcF90ZXJtc19hbGwkcmVzdWx0JHNvdXJjZVt0b3BfdGVybXNfYWxsJHJlc3VsdCR0ZXJtX3NpemUgPCA1MDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcF90ZXJtc19hbGwkcmVzdWx0JHRlcm1fc2l6ZSA+IDJdCikKCmtuaXRyOjprYWJsZSh0b3BfdGVybXNbMToxMCxdLCBjYXB0aW9uID0gIlRvcCBnZW5lc2V0cyB1c2luZyBsaXN0IG9mIGFsbCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMiKSAlPiUga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZygic3RyaXBlZCIpCmBgYAoKYGBge3IgdG9wX3Rlcm1fcGVyX2NhdGVnb3J5LCBtZXNzYWdlPUZBTFNFfQprbml0cjo6a2FibGUocmJpbmQodG9wX3Rlcm1zW3RvcF90ZXJtcyRzb3VyY2UgPT0gIkdPOkJQIixdWzEsXSwKICAgICAgICAgICAgICAgICAgIHRvcF90ZXJtc1t0b3BfdGVybXMkc291cmNlID09ICJHTzpNRiIsXVsxLF0sCiAgICAgICAgICAgICAgICAgICB0b3BfdGVybXNbdG9wX3Rlcm1zJHNvdXJjZSA9PSAiS0VHRyIsXVsxLF0pLAogICAgICAgICAgICAgY2FwdGlvbiA9ICJUb3AgdGVybXMgZnJvbSBlYWNoIGRhdGEgc291cmNlIHVzaW5nIGxpc3Qgb2YgYWxsIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyIpICU+JQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoInN0cmlwZWQiKQpgYGAKYGBge3IgYWxsX2Rpc3RfcGxvdH0KZ3Byb2ZpbGVyMjo6Z29zdHBsb3QodG9wX3Rlcm1zX2FsbCkgJT4lIHBsb3RseTo6bGF5b3V0KHRpdGxlID0gIk1hbmhhdHRhbiBwbG90IHNob3dpbmcgZGlzdHJpYnV0aW9uIG9mIHRlcm1zIGZyb20gZWFjaCBkYXRhIHNvdXJjZSIsIGZvbnQgPSBsaXN0KHNpemUgPSAxMCkpCmBgYAoKYGBge3IgY291bnRfdG9wX3Rlcm1zLCBtZXNzYWdlPUZBTFNFfQpsZW5ndGgodG9wX3Rlcm1zJHRlcm1fbmFtZSkKYGBgCgojIyBEaXNjdXNzaW9uCgoxLiBXZSB1c2VkIGc6UHJvZmlsZXIgYXMgd2UgaGF2ZSBleHRlbnNpdmVseSBkaXNjdXNzZWQgYWJvdXQgaXQgaW4gY2xhc3MuIEl0IGhhcyBhIG5pY2Ugd2ViLWJhc2VkIGludGVyZmFjZSBhcyB3ZWxsIGFzIGVhc3ktdG8tdXNlIEFQSXMgaW4gdGhlIGZvcm0gb2YgYW4gUiBwYWNrYWdlLiBGdXJ0aGVybW9yZSwgdGhlIGRhdGEgc291cmNlcyBvbiBnOlByb2ZpbGVyIGlzIGZyZXF1ZW50bHkgdXBkYXRlZCBhbmQgaW5jbHVkZSB0aGUgZGF0YSBzb3VyY2VzIHRoYXQgd2UgYXJlIG1vc3QgaW50ZXJlc3RlZCBpbi4KCjIuIFdlIHVzZWQgR08gQmlvbG9naWNhbCBQcm9jZXNzLCBHTyBNb2xlY3VsYXIgRnVuY3Rpb24sIGFuZCBLRUdPLiBXZSBjaG9zZSB0aGVzZSBkYXRhIHNvdXJjZXMgc2luY2UgdGhleSBhcmUgYWxzbyB1c2VkIGJ5IHRoZSBhdXRob3Igb2YgdGhlIG9yaWdpbmFsIHBhcGVyIGZyb20gd2hpY2ggSSBvYnRhaW5lZCB0aGUgZGF0YXNldC4gSG93ZXZlciwgc2luY2UgS0VHTyBpcyBhIGNvbW1lcmNpYWwgZGF0YSBzb3VyY2UsIGlmIGl0IHdhcyBub3QgZm9yIHRoYXQgZmFjdCB0aGF0IHRoZSBvcmlnaW5hbCBwYXBlciB1c2VkIGl0LCBJIHdvdWxkIHBlcnNvbmFsbHkgcmVmcmFpbiBmcm9tIHVzaW5nIGl0LiBUaGUgb3JpZ2luYWwgYXV0aG9yIGFsc28gdXNlZCBvdGhlciBkYXRhIHNvdXJjZXMgaW5jbHVkaW5nIENhbm9uaWNhbCBQYXRod2F5cyBidXQgc2luY2UgaXQgaXMgbm90IHBhcnQgb2YgZzpQcm9maWxlciwgd2UgZGlkIG5vdCBpbmNsdWRlIHRoZXNlIGRhdGEgc291cmNlcy4gVGhlIGFubm90YXRpb24gc291cmNlIHZlcnNpb25zIGFyZSBhcyBmb2xsb3dzOiBFbnNlbWJsIDEwNSwgRW5zZW1ibCBHZW5vbWVzIDUyIChkYXRhYmFzZSBidWlsdCBvbiAyMDIyLTAyLTE0KQoKMy4gRm9yIGFsbCB0aHJlZSBhbmFseXNpcyAodXNpbmcgdXByZWd1bGF0ZWQsIGRvd25yZWd1bGF0ZWQsIGFsbCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQpLCB3ZSB1c2VkIGEgdGhyZXNob2xkIGJldHdlZW4gMiBhbmQgNTAwLiBXZSBzZXQgdGhlIHVwcGVyIGJvdW5kIHRvIDUwMCBiZWNhdXNlIHdlIGRvIG5vdCB3YW50IHRvIGluY2x1ZGUgb3Zlcmx5IGJyb2FkIGFuZCBnZW5lcmljIHRlcm1zIHRoYXQgd2lsbCBub3QgZ2l2ZSB1cyBtZWFuaW5nZnVsIGluc2lnaHRzIGludG8gdGhlIHJvbGVzIG9mIHRoZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuIFRoZSBzZXQgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgcmV0dXJuZWQgNDMzIGdlbmVzZXRzOyB0aGUgc2V0IG9mIGRvd25yZWd1bGF0ZWQgZ2VuZXMgcmV0dXJuZWQgNTYwIGdlbmVzZXRzOyB0aGUgc2V0IG9mIGFsbCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgcmV0dXJuZWQgNjE2IGdlbmUgc2V0cy4KCjQuIFRoZSByZXN1bHQgdXNpbmcgdGhlIHdob2xlIGxpc3QgKHNldCBvZiBhbGwgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzKSBpcyBtb3JlIHByZWRvbWluYW50bHkgcmVwcmVzZW50ZWQgYnkgdGhlIGRvd25yZWd1bGF0ZWQgZ2VuZXMuIEl0IGFsc28gZG9lcyBub3QgcHJvdmlkZSBhIGxvdCBvZiBpbnNpZ2h0cyBpbnRvIHRoZSByb2xlcyBvZiB0aGUgZ2VuZXMgYXMgaXQgaXMgYSBtaXggb2YgZHJhc3RpY2FsbHkgZGlmZmVyZW50IGFuZCBzZWVtaW5nbHkgdW5yZWxhdGVkIHRlcm1zIChlLmcuIG1pdG90aWMgbnVjbGVhciBkaXZpc2lvbiwgb3JnYW5lbGxlIGZpc3Npb24sIHJlc3BvbnNlIHRvIGVuZG9wbGFzbWljIHJldGljdWx1bSBzdHJlc3MsIGV0Yy4pLiBIb3dldmVyLCBieSB1c2luZyBzZXBhcmF0aW5nIGludG8gdXByZWd1YWx0ZWQgYW5kIGRvd25yZWd1bGF0ZWQgZ2VuZXMgYW5kIHBlcmZvcm1pbmcgdGhlIGdlbmUgZW5yaWNobWVudCBhbmFseXNpcyBzZXBhcmF0ZWx5LCB3ZSBnZXQgbW9yZSBtZWFuaW5nZnVsIHRlcm1zOiB0aGUgdXByZWd1YWx0ZWQgZ2VuZXMgYXJlIG1vc3RseSBhc3NvY2lhdGVkIHdpdGggcGF0aHdheXMgYW5kIHByb2Nlc3NlcyBpbnZvbHZpbmcgZW5kb3BsYXNtaWMgcmV0aWN1bHVtIHdoZXJlYXMgdGhlIGRvd25yZWd1bGF0ZWQgZ2VuZXMgYXJlIG1vc3RseSBhc3NvY2lhdGVkIHdpdGggdGhlIGNlbGwgY3ljbGUuCgoKIyBJbnRlcnByZXRhdGlvbgoKMS4gWWVzLiBUaGUgb3ZlcnJlcHJlc2VudGF0aW9uIGFuYWx5c2lzIHJlc3VsdHMgc3VwcG9ydCB0aGUgY29uY2x1c2lvbiBkaXNjdXNzZWQgaW4gdGhlIG9yaWdpbmFsIHBhcGVyLiBUaGUgb3JpZ2luYWwgcGFwZXIgY2xhaW1zIHRoYXQgQ0JEIHRyZWF0bWVudCBjYW4gaW5oaWJpdCBTQVJTLUNvVi0yIHJlcGxpY2F0aW9uIHRocm91Z2ggdGhlIGhvc3QncyBFUiBzdHJlc3MgcmVzcG9uc2UgcGF0aHdheS4gQWx0aG91Z2ggd2Ugd2VyZSBub3QgYWJsZSB0byB2ZXJpZnkgd2hldGhlciBvciBub3QgdGhpcyBoYXMgYW55IGFmZmVjdCBvbiBTQVJTLUNvVi0yIHJlcGxpY2F0aW9uLCB3ZSBhcmUgYWJsZSB0byBjb25maXJtIGJhc2VkIG9uIHRoZSB0b3AgdGVybXMgcmV0dXJuZWQgYnkgdGhlIG92ZXJyZXByZXNlbnRhdGlvbiBhbmFseXNpcyB0aGF0IHRoZSBnZW5lcyBhc3NvY2lhdGVkIHdpdGggRVIgc3RyZXNzIHJlc3BvbnNlIGFyZSBpbmRlZWQgdXByZWd1bGF0ZWQgaW4gdGhlIHNhbXBsZXMgdHJlYXRlZCB3aXRoIENCRC4KCjIuIFRoZSBjb25jbHVzaW9uIHRoYXQgQ0JEIGlzIGNvcnJlbGF0ZWQgdG8gcmVkdWNlZCByaXNrIG9yIHNldmVyaXR5IG9mIFNBUlMtQ29WLTIgaXMgc3VwcG9ydGVkIGJ5IHRlc3RpbmcgYWR1bHQgcGF0aWVudHMgYXMgcHJlc2VudGVkIGJ5IHRoZSBvcmlnaW5hbCBwYXBlci4gVGhlcmUgYXJlIGFsc28gb3RoZXIgcGFwZXJzIHdobyBkaXNjdXNzZWQgdGhlIHJvbGUgb2YgQ0JEIGluIFNBUlMtQ29WLTIgaW5mZWN0aW9uICh2YW4gQnJlZW1hbiBldCBhbC4pLiBUaGUgcmVzdWx0IGZyb20gb3VyIG92ZXJyZXByZXNlbnRhdGlvbiBhbmFseXNpcyBpcyBzdXBwb3J0ZWQgYnkgdGhlIG9yaWdpbmFsIHBhcGVyLCBpbiB3aGljaCB0aGUgYXV0aG9yIGhhcyBhbHNvIHBlcmZvcm1lZCBnZW5lIGVucmljaG1lbnQgYW5hbHlzaXMgd2l0aCBzaW1pbGFyIG91dGNvbWVzLiBIb3dldmVyLCBzaW5jZSB0aGlzIGlzIHN0aWxsIGFuIGVhcmx5IHJlc2VhcmNoIGluIHRoaXMgdG9waWMgYW5kIFNBUlMtQ29WLTIgY29udGludWVzIHRvIGV2b2x2ZSwgdGhlcmUgZG9lcyBub3Qgc2VlbSB0byBiZSBvdGhlciB1bnJlbGF0ZWQgZ3JvdXBzIHdobyBjb3VsZCBwcm92aWRlIGFkZGl0aW9uYWwgc3VwcG9ydGluZyBldmlkZW5jZSB0byBvdXIgcmVzdWx0cyBmcm9tIHRoZSBvdmVycmVwcmVzZW50YXRpb24gYW5hbHlzaXMgYmVzaWRlcyB0aGUgb3JpZ2luYWwgcGFwZXIuCgojIEpvdXJuYWwKCkxpbmsgdG8gbXkgam91cm5hbCBlbnRyeSBmb3IgdGhpcyBhc3NpZ25tZW50OiBodHRwczovL2dpdGh1Yi5jb20vYmNiNDIwLTIwMjIvS2V2aW5fR2FvL3dpa2kvQXNzaWdubWVudC0yCgojIFJlZmVyZW5jZXMKCi0gQ2hlbiBZLCBMdW4gQVRMLCBTbXl0aCBHSyAoMjAxNikuIEZyb20gcmVhZHMgdG8gZ2VuZXMgdG8gcGF0aHdheXM6IGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIG9mIFJOQS1TZXEgZXhwZXJpbWVudHMgdXNpbmcgUnN1YnJlYWQgYW5kIHRoZQogIGVkZ2VSIHF1YXNpLWxpa2VsaWhvb2QgcGlwZWxpbmUuIEYxMDAwUmVzZWFyY2ggNSwgMTQzOAotIER1cmluY2ssIFMuLCBTcGVsbG1hbiwgUC4gVC4sIEJpcm5leSwgRS4sICYgSHViZXIsIFcuICgyMDA5KS4gTWFwcGluZyBpZGVudGlmaWVycyBmb3IgdGhlIGludGVncmF0aW9uIG9mIGdlbm9taWMgZGF0YXNldHMgd2l0aCB0aGUgUi9CaW9jb25kdWN0b3IgcGFja2FnZSBiaW9tYVJ0LiBOYXR1cmUgLSBwcm90b2NvbHMsIDQoOCksIDExODTigJMxMTkxLiBodHRwczovL2RvaS5vcmcvMTAuMTAzOC9ucHJvdC4yMDA5Ljk3Ci0gTWFydGluIE1vcmdhbiAoMjAyMSkuIEJpb2NNYW5hZ2VyOiBBY2Nlc3MgdGhlIEJpb2NvbmR1Y3RvciBQcm9qZWN0IFBhY2thZ2UgUmVwb3NpdG9yeS4gUiBwYWNrYWdlIHZlcnNpb24gMS4zMC4xNi4KICBodHRwczovL0NSQU4uUi1wcm9qZWN0Lm9yZy9wYWNrYWdlPUJpb2NNYW5hZ2VyCi0gTWNDYXJ0aHkgREosIENoZW4gWSBhbmQgU215dGggR0sgKDIwMTIpLiBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBvZiBtdWx0aWZhY3RvciBSTkEtU2VxIGV4cGVyaW1lbnRzIHdpdGggcmVzcGVjdCB0byBiaW9sb2dpY2FsCiAgdmFyaWF0aW9uLiBOdWNsZWljIEFjaWRzIFJlc2VhcmNoIDQwLCA0Mjg4LTQyOTcKLSBOZ3V5ZW4sIEwuIEMuLCBZYW5nLCBELiwgTmljb2xhZXNjdSwgVi4sIEJlc3QsIFQuIEouLCBPaHRzdWtpLCBULiwgQ2hlbiwgUy4tTi4sIEZyaWVzZW4sIEouIEIuLCBEcmF5bWFuLCBOLiwgTW9oYW1lZCwgQS4sIERhbm4sIEMuLCBTaWx2YSwgRC4sIEd1bGEsIEguLCBKb25lcywgSy4gQS4sIE1pbGxpcywgSi4gTS4sIERpY2tpbnNvbiwgQi4gQy4sIFRheSwgUy4sIE9ha2VzLCBTLiBBLiwgUGF1bGksIEcuIEYuLCBNZWx0emVyLCBELiBPLiwg4oCmIFJvc25lciwgTS4gUi4gKDIwMjEpLiBDYW5uYWJpZGlvbCBpbmhpYml0cyBTQVJTLUNPVi0yIHJlcGxpY2F0aW9uIGFuZCBwcm9tb3RlcyB0aGUgaG9zdCBpbm5hdGUgaW1tdW5lIHJlc3BvbnNlLiBTY2llbmNlIEFkdmFuY2VzLiBodHRwczovL3d3dy5zY2llbmNlLm9yZy9kb2kvYWJzLzEwLjExMjYvc2NpYWR2LmFiaTYxMTAKLSBSb2JpbnNvbiBNRCwgTWNDYXJ0aHkgREogYW5kIFNteXRoIEdLICgyMDEwKS4gZWRnZVI6IGEgQmlvY29uZHVjdG9yIHBhY2thZ2UgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIG9mIGRpZ2l0YWwgZ2VuZSBleHByZXNzaW9uIGRhdGEuCiAgQmlvaW5mb3JtYXRpY3MgMjYsIDEzOS0xNDAKLSBHdSwgWi4gKDIwMTYpIENvbXBsZXggaGVhdG1hcHMgcmV2ZWFsIHBhdHRlcm5zIGFuZCBjb3JyZWxhdGlvbnMgaW4gbXVsdGlkaW1lbnNpb25hbCBnZW5vbWljIGRhdGEuIEJpb2luZm9ybWF0aWNzLgotIEtvbGJlcmcgTCwgUmF1ZHZlcmUgVSwgS3V6bWluIEksIFZpbG8gSiwgUGV0ZXJzb24gSCAoMjAyMCkuIOKAnGdwcm9maWxlcjItIGFuIFIgcGFja2FnZSBmb3IgZ2VuZSBsaXN0IGZ1bmN0aW9uYWwgZW5yaWNobWVudAphbmFseXNpcyBhbmQgbmFtZXNwYWNlIGNvbnZlcnNpb24gdG9vbHNldCBnOlByb2ZpbGVyLuKAnSBfRjEwMDBSZXNlYXJjaF8sICo5IChFTElYSVIpKig3MDkpLiBSIHBhY2thZ2UgdmVyc2lvbiAwLjIuMS4KLSBTdGVmYW4gTWlsdG9uIEJhY2hlIGFuZCBIYWRsZXkgV2lja2hhbSAoMjAyMCkuIG1hZ3JpdHRyOiBBIEZvcndhcmQtUGlwZSBPcGVyYXRvciBmb3IgUi4gaHR0cHM6Ly9tYWdyaXR0ci50aWR5dmVyc2Uub3JnLAogIGh0dHBzOi8vZ2l0aHViLmNvbS90aWR5dmVyc2UvbWFncml0dHIuCi0gdmFuIEJyZWVtZW4sIFIuIEIuLCBNdWNoaXJpLCBSLiBOLiwgQmF0ZXMsIFQuIEEuLCBXZWluc3RlaW4sIEouIEIuLCBMZWllciwgSC4gQy4sIEZhcmxleSwgUy4sICYgVGFmZXNzZSwgRi4gRy4gKDIwMjIpLiBDYW5uYWJpbm9pZHMgQmxvY2sgQ2VsbHVsYXIgRW50cnkgb2YgU0FSUy1Db1YtMiBhbmQgdGhlIEVtZXJnaW5nIFZhcmlhbnRzLiBKb3VybmFsIG9mIG5hdHVyYWwgcHJvZHVjdHMsIDg1KDEpLCAxNzbigJMxODQuIGh0dHBzOi8vZG9pLm9yZy8xMC4xMDIxL2Fjcy5qbmF0cHJvZC4xYzAwOTQ2